package se.webbzon.boltzmann.game.sequence;

import se.webbzon.oschi01.worldobject.WorldObject;

public class AssignmentSequenceNode extends SequenceNode {
	
	private static final String operations = "+-*/%&";
	
	//private static final String regex = "[\\p{+}-\\p{*}/%& ]";
	
	// An optional next sequence node
	private SequenceNode next;
	
	// The variable name and the value of the assignment
	private final String variable, expression;

	/*============================================================
	Constructors
	============================================================*/
	
	/** Creates a new assignment node which assigns the parsed value
	 * of the provided expression to the environment variable with 
	 * the given name. The expression must be given using prefix notation. **/
	public AssignmentSequenceNode(Sequence sequence, String variable, String expression) {
		super(sequence);
		this.variable = variable;
		this.expression = expression;
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Sets an optional branch for this assignment sequence node. **/
	public void setBranch(SequenceNode next) {
		this.next = next;
	}

	@Override public int branches() {
		return (next == null) ? 0 : 1;
	}

	@Override public SequenceNode getBranch(int n) {
		return next;
	}

	@Override protected void onBegin(WorldObject binding) {
		if (!expression.isEmpty()) {
			final Sequence sequence = getSequence();
			String[] nodes = expression.split(" ");
			Result result = parse(nodes,0);
			if (result.result == null) {
				Sequences.set(sequence, variable, (String) null);
				Sequences.set(sequence, variable, (Double) null);
			} else if (result.result instanceof String)
				Sequences.set(sequence, variable, (String) result.result);
			else
				Sequences.set(sequence, variable, (Double) result.result);
		}
		getSequence().next(0);
	}

	@Override protected void onEnd(WorldObject binding) {};
	
	/*============================================================
	Private Methods
	============================================================*/
	
	/** Parses the value from the given string into either
	 * another string or a double. **/
	private final Result parse(String[] nodes, int offset) {
		String str = nodes[offset];
		/** Get the prefix **/
		char c = str.charAt(0);
		if (operations.indexOf(c) >= 0 && str.length() == 1) {
			Result first = parse(nodes,offset+1);
			Result rest = parse(nodes,first.newOffset);
			int newOffset = rest.newOffset;
			Object result = null;
			switch (c) {
			case '+':
				result = (Double) first.result + (Double) rest.result;
				break;
			case '-':
				result = (Double) first.result - (Double) rest.result;
				break;
			case '*':
				result = (Double) first.result * (Double) rest.result;
				break;
			case '/':
				result = (Double) first.result / (Double) rest.result;
				break;
			case '%':
				result = (Double) first.result % (Double) rest.result;
				break;
			case '&':
				result = (String) first.result + (String) rest.result;
				break;
			}
			return new Result(result,newOffset);
			
		} else {
			return new Result(	Sequences.parse(getSequence(),nodes[offset]),
								offset + 1);
		}
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	private static final class Result {
		
		private final Object result;
		private final int newOffset;
		
		public Result(Object result, int newOffset) {
			this.result = result;
			this.newOffset = newOffset;
		}
	}

}
